package com.gite.nouette.admin.implementation.service;

import java.util.List;

import com.gite.nouette.admin.client.Constants;
import com.gite.nouette.admin.contract.data.IJsonRpcRequest;
import com.gite.nouette.admin.contract.data.IJsonRpcResponse;
import com.gite.nouette.admin.contract.data.ILog;
import com.gite.nouette.admin.contract.service.ICallback;
import com.gite.nouette.admin.contract.service.IServiceASync;
import com.gite.nouette.admin.implementation.data.JsonRpcRequest;
import com.gite.nouette.admin.implementation.service.exception.HTTPServiceException;
import com.gite.nouette.admin.implementation.service.exception.JSONRpcServiceException;
import com.gite.nouette.admin.implementation.service.exception.ServiceException;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;

public class ServiceASyncJSon implements IServiceASync {

	private Constants constants = GWT.create(Constants.class);

	@Override
	public void getLogs(ICallback<List<ILog>> callback) {
		IJsonRpcRequest jsonRpcRequest = new JsonRpcRequest();
		jsonRpcRequest.setJsonrpc("2.0");
		jsonRpcRequest.setMethod("getLogs");
		jsonRpcRequest.setId("1");

		Meta<List<ILog>> metaResult = new Meta<List<ILog>>(List.class);
		metaResult.putPropertyMeta("", new Meta<ILog>(ILog.class));

		sendControlProxy(constants.service_log_url(), jsonRpcRequest, callback,
				metaResult);
	}

	private <R> void sendControlProxy(String serviceUrl,
			IJsonRpcRequest jsonRpcRequest, ICallback<R> callback,
			Meta<R> metaResult) {
		String url = serviceUrl;
		if (Boolean.parseBoolean(constants.service_proxy_enabled())) {
			url = constants.proxy_url() + "?url=" + url;
		}
		send(url, jsonRpcRequest, callback, metaResult);
	}

	private <R> void send(String url, IJsonRpcRequest jsonRpcRequest,
			ICallback<R> callback, Meta<R> metaResult) {
		DataToJson dataToJson = new DataToJson();
		JSONValue jsonValue = dataToJson.getJsonValue(jsonRpcRequest);
		RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
				URL.encode(url));
		try {
			builder.sendRequest(jsonValue.toString(),
					createRequestCallback(callback, metaResult));
		} catch (RequestException e) {
			throw new ServiceException(url, jsonValue.toString(), e);
		}

	}

	private <R> RequestCallback createRequestCallback(
			final ICallback<R> callback, final Meta<R> metaResult) {
		return new RequestCallback() {
			@Override
			public void onError(Request request, Throwable exception) {
				callback.onError(exception);
			}

			@Override
			public void onResponseReceived(Request request, Response response) {
				if (200 == response.getStatusCode()) {
					JSONValue jsonValue = JSONParser.parseStrict(response
							.getText());
					JsonToData jsonToData = new JsonToData();
					Meta<IJsonRpcResponse<R>> metaJsonRpcResponse = new Meta<IJsonRpcResponse<R>>(
							IJsonRpcResponse.class);
					metaJsonRpcResponse.putPropertyMeta("result", metaResult);
					IJsonRpcResponse<R> jsonRpcResponse = jsonToData.getData(
							jsonValue, metaJsonRpcResponse);
					if (jsonRpcResponse.getError() == null) {
						callback.onDone(jsonRpcResponse.getResult());
					} else {
						callback.onError(new JSONRpcServiceException(
								jsonRpcResponse.getError(), request.toString(),
								response.getText()));
					}
				} else {
					callback.onError(new HTTPServiceException(response
							.getStatusCode(), request.toString(), response
							.getText()));
				}
			}
		};
	}

}
